load("//node:internal/providers.bzl", "NodeModuleInfo", "NodeModulesInfo")


def _get_filename_relative_to_module(module, file):
    parts = file.path.partition("/%s/" % module.name)
    return '/'.join(parts[1:])


def _copy_module(ctx, target_dir, module):
    filelist = module.files.to_list()
    if len(filelist) == 0:
        return []

    inputs = []
    outputs = []

    script_file = ctx.actions.declare_file('%s/copy_%s.sh' % (ctx.label.name, module.identifier))
    #script_file = ctx.actions.declare_file('copy_%s.sh' % (module.identifier))
    script_lines = []

    for src in filelist:
        inputs.append(src)
        dst_filename = _get_filename_relative_to_module(module, src)
        dst = ctx.actions.declare_file('%s/node_modules/%s' % (target_dir, dst_filename))
        outputs.append(dst)
        script_lines.append("cp '%s' '%s'" % (src.path, dst.path))

    ctx.actions.write(
        output = script_file,
        content = '\n'.join(script_lines),
        is_executable = True,
    )

    ctx.actions.run_shell(
        mnemonic = 'CopyModuleWith%sFiles' % len(outputs),
        inputs = inputs + [script_file],
        outputs = outputs,
        command = script_file.path,
    )

    return outputs


def _copy_modules(ctx, target_dir, deps):
    outputs = []
    for dep in deps:
        module = dep[NodeModuleInfo]
        outputs += _copy_module(ctx, target_dir, module)
        for module in module.transitive_deps.to_list():
            outputs += _copy_module(ctx, target_dir, module)
    return outputs

    
def node_modules_impl(ctx):
    target_dir = ctx.attr.target
    files = _copy_modules(ctx, target_dir, ctx.attr.deps)
    manifest = ctx.actions.declare_file('%s/node_modules/manifest.json' % target_dir)
    files.append(manifest)

    modules = []
    for dep in ctx.attr.deps:
        module = dep[NodeModuleInfo]
        modules.append(module)
        for m in module.transitive_deps.to_list():
            modules.append(m)

    json = {}
    dependencies = {}
    for module in modules:
        dependencies[module.name] = module.version
        json['dependencies'] = struct(**dependencies)

    manifest_content = struct(**json)

    ctx.actions.write(
        output = manifest,
        content = manifest_content.to_json(),
    )

    return [DefaultInfo(
        files = depset(files),
        runfiles = ctx.runfiles(
            files = files,
            collect_data = True,
        ),        
    ), NodeModulesInfo(
        files = files,
        modules = depset(modules),
        manifest = manifest,
    )]


node_modules = rule(
    node_modules_impl,
    attrs = {
        'deps': attr.label_list(
            providers = [NodeModuleInfo],
        ),
        'target': attr.string(
            mandatory = True,
        ),
    }
)
